<?php
declare (strict_types=1);

namespace app\admin\controller;

use app\admin\model\GoodsPics;
use Dotenv\Exception\ValidationException;
use think\facade\Db;
use think\facade\Log;
use think\exception\ValidateException;
use think\facade\Filesystem;
use think\facade\Session;
use think\facade\Validate;
use think\facade\View;
use think\Image;
use think\Request;

use app\admin\model\Goods as GoodsModel;

// 使用模型、避免冲突、起一个别名

/**
 * Class Goods
 * @package app\admin\controller
 * 使用模型、操作数据
 * 项目都是迭代开发
 * 围绕商品模块开发各种功能以及知识点
 */

class Goods
{
    /**
     * 查询、添加、删除、修改
     * 展示商品列表
     * @return string
     */
    public function list(Request $request)
    {

        # 查询数据、列表多条数据
        # select()查询所有的数据
        $list = GoodsModel::select();
        # 只查询 id是38 和135 、存在的数据
//        $list = GoodsModel::select([38,135]);
        # 不存在的数据
//        $list = GoodsModel::select([1,2]);
//        dd($list);

        # 数据发送到模版进行渲染

        # TP和laravel框架、框架解析流程、封装的方法
//        语法不一样、参考对应手册
        // 分页怎么查找？
        // 怎么合理渲染列表数据？

        # 添加搜索条件
//        $goods_name = $request->get('goods_name');
//        $goods_name = $request->param('goods_name');
        $goods_name = input('goods_name');
        $goods_status = input('goods_status');// 0 或着 1

        $where = [];
//        是否有搜索条件
        if (!empty($goods_name)) {
            $where[] = ["goods_name",'like',"$goods_name%"];
        }

        // 判断是否传送该参数
        if (\request()->has('goods_status')) {
            $where[] = ["goods_status",'=',$goods_status];
        }

//        ...还可以加分类、品牌、以及其他搜索条件

        # 普通的分页
//        $list = GoodsModel::where('goods_name','like',"%$goods_name%")->paginate(3);//分页查询数据\

        # 保留搜索条件并分页
        # 高亮显示、自己完成一下 str_replace
        # 最新的展示到最前面 可以根据id排序 desc 降序、也可以根据创建时间排序
        $list = GoodsModel::where($where)->order('id','desc')->paginate([
            'list_rows' => 3,// 每页显示的数量
            'query' => [
                'goods_name' => $goods_name,
                'goods_status' => $goods_status
            ],//额外的查询、条件是数组
        ]);

//        高亮替换
        foreach ($list as $key=> $value) {
            $list[$key]['goods_name'] = str_replace($goods_name,"<font color='red'>$goods_name</font>",$value['goods_name']);
        }
        # 有搜索条件需要、发送至模版进行渲染
        View::assign('goods_name', $goods_name);// 为了在表单里面显示搜索
        View::assign('goods_status', $goods_status);// 为了在表单里面显示搜索
        View::assign("data", $list);//发送的是一个数据集合/对象
//        等价于
//        View::assign([
//            'goods_name' => $goods_name,
//            'data' => $list
//        ]);

        return View::fetch('admin/goods_list');
    }

    /**
     * 商品添加页面
     */
    public function add()
    {
        return View::fetch('admin/goods_add');
    }

    /**
     * 实现商品添加功能
     */
    public function insert(Request $request)
    {
        # 测试访问是否联通
//        return 'success';
        # 接收表单的数据
        $param = $request->all();
        // 唯一验证的之前过滤
        $param['goods_name'] = trim($param['goods_name']);//去除特殊字符
        # 验证表单的参数
//        第一步 创建验证器 php think make:validate admin@Goods
        // 第二步 配置验证信息
//        rule 验证规则 message 验证不通过的错误信息
        // 使用什么验证器、验证什么参数 goods验证器类型、验证表单数据、后续继续完善
        try {
            // 第三步 使用验证器
            validate(\app\admin\validate\Goods::class)->check($param);
        } catch (ValidateException $exception) {
            dd($exception->getError());
        }

        # 文件上传（相册、goods_logo）

//        就是直接把缩略图路径保存放到数据库里对吧
        /**
         *  方法一 （上传成功之后、进行制作缩略图、简单）
         */
        # 打开原图片
//        $file = \request()->file('goods_logo');//接收文件的信息
//        $goods_logo = Filesystem::disk('public')->putFile('image', $file);// 返回的结果、保存到public 可以直接访问
//        $image = Image::open($goods_logo);
//        # 生成新的缩略图、可以覆盖原图
//        $image->thumb(80,80)->save($goods_logo);
        /**
         * 方法二 （直接接收文件上传的信息、制作缩略图、复杂）//
         */
//        制作缩略图
        $image = Image::open(\request()->file('goods_logo'));
        // 怎么获取文件上传后缀
        // getOriginalName()获取到文件名字
        $file_name= \request()->file('goods_logo')->getOriginalName();
        //pathinfo() 函数以数组的形式返回关于文件路径的信息

         //可以直接使用文件名称 方法一
        $old_file_name= pathinfo($file_name)['basename'];
        // 也可以系统生成文件名 方法二
//        $ext = pathinfo($file_name)['extension'];
        //不能直接放老图片上传那个路径吗?
        // 上传的图片有jpg、png、jpeg
        // 商品logo是生成一个80*80的缩略图、并添加"2002A"文字水印
       $image->thumb(120,120)->save('./storage/'.$old_file_name);

        # 添加入库
        # 怎么保证数据的一致性？事务 要么全部成功、要么全部失败
        # 添加、或者修改、删除、操作多张表、两张表以及两表以上
        # 保证商品表添加成、商品相册表也需要成功
        // 启动事务
        Db::startTrans();
        try {
            # 第一种方法、需要实例化模型
            # 添加商品表
            $goodsModel = new GoodsModel();
            $goodsModel->save([
                'goods_name' => $param['goods_name'],
                'goods_price' => $param['goods_price'],
                'goods_logo' => '/storage/' . $old_file_name,
                'goods_introduce' => $param['goods_desc']
            ]);
            # 获取商品id
            $goods_id = $goodsModel->id;
            # 商品相册 多个图片、格式是数组
            # 返回的图片路径、怎么处理？
            # 表 goods_pics相册表 大图地址 小图地址
            $goods_pics = [];// 构造入库的数据
            foreach ($param['goods_pics'] as $value) {
                # 图像处理
                $pics_path = './storage/'.Filesystem::disk('public')->putFile('image', $value);// 返回的结果、保存到public 可以直接访问
                $image = Image::open($pics_path);//打开是我们大图的图片

                $sma_path = './storage/image/'.date('Ymd').'/pics_sma_'.pathinfo($pics_path)['basename'];//小图地址
                $image->thumb(40,40)->save($sma_path);

                // 处理路径、保存入库的路径
                $goods_pics[] = [
                    'goods_id' => $goods_id,//原图视为大图
                    'pics_big' => trim($pics_path,'.'),//原图视为大图
                    'pics_sma' => trim($sma_path,'.'),//利用大图生成小图
                ];
            }

            # 保存入库、批量添加数据
            $goodsPicsModel = new GoodsPics();
            $goodsPicsModel->saveAll($goods_pics);

            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            dd("商品添加失败".$e->getMessage());
        }


        # 第二钟方法、第三种方法

        # 记录添加成功日志
        Log::info(Session::get('username')."添加商品".$goodsModel->id);

        # 跳转商品列表
        return redirect('/admin/goodsList');

    }

    /**
     * 删除商品
     * @param Request $request
     * @return \think\response\Json
     */
    public function delete(Request $request)
    {
        $id = $request->post('id');
        if (is_array($id)) {
            // destory() 可以传主键、也可以传数组
            $result = \app\admin\model\Goods::destroy([1,2,3]);
            return json(['code' => 200, 'msg' => 'ok', 'data' => []]);
        }
        # 根据主键删除的是数字
//        if (!is_numeric($id)) {
//            return json(['code' => 2001,'msg' => '参数格式不正确','data' => []]);
//        }
//        通过验证器验证规则
        $validate = Validate::rule(['id' => 'require|number'])->message(['id.require' => '商品id必填']);
        $result = $validate->check(['id' => $id]);
        # 验证不通过
        if ($result != true) {
            return json(['code' => 200, 'msg' => $validate->getError(), 'data' => []]);
        }
        # 方法一
//        $goods = \app\admin\model\Goods::find($id);
//        $result = $goods->delete();// 删除后的结果
        # 方法二
        $result = \app\admin\model\Goods::destroy($id);

        if ($result) {
            return json(['code' => 200, 'msg' => 'ok', 'data' => []]);

        } else {
            return json(['code' => 2001, 'msg' => '删除失败', 'data' => []]);
        }
    }

    /**
     * 修改状态
     * @param Request $request
     * @return \think\response\Json
     */
    public function updateStatus(Request $request)
    {
        $id = $request->post('id');
        # 根据主键删除的是数字
        if (!is_numeric($id)) {
            return json(['code' => 2001, 'msg' => '参数格式不正确', 'data' => []]);
        }

        # 修改信息 判断条件? 真: 假;
        # 第一种方法
        $goods = \app\admin\model\Goods::find($id);
        # 上架商品之前、需要核对商品是否正确、是否审核通过、业务逻辑
        # 如果是上架、则改为下架、否则改为上架
        if ($goods->goods_status == 1) {
            $status = 0;
        } else {
            $status = 1;
        }
        $goods->goods_status = $status; // 更新的数据、重新复制
//        $goods->goods_status   = $goods->goods_status == 1 ? 0 : 1;
        $result = $goods->save();
        # 第二方法、需要知道状态值
//        update()


        if ($result) {
            return json(['code' => 200, 'msg' => 'ok', 'data' => []]);

        } else {
            return json(['code' => 2001, 'msg' => '更新成功', 'data' => []]);
        }
    }

    // 商品更新

    /**
     * 展示更新数据
     * 商品更新
     */
    public function goodsEditor(Request $request)
    {
        # 需要知道更新的哪条数据
        $id = $request->get('id');
        # 根据主键删除的是数字
        if (!is_numeric($id)) {
            return json(['code' => 2001, 'msg' => '参数格式不正确', 'data' => []]);
        }
        # 查询数据
        $info = \app\admin\model\Goods::find($id);
        # 渲染数据
        View::assign('info', $info);
        return View::fetch('admin/goods_edit');
    }

    /**
     * 更新数据
     */
    public function update(Request $request)
    {
        # 测试访问是否联通
//        return 'success';
        # 接收表单的数据
        $param = $request->all();

        try {
            if (isset($param['goods_logo'])) {
                echo "修改了文件上传";
                # 修改了文件
                # 走修改的验证场景
                validate(\app\admin\validate\Goods::class)->check($param);
                // 重新上传
                $goods_logo = Filesystem::disk('public')->putFile('image', $request->file('goods_logo'));
                $param['goods_logo'] = '/storage/' . $goods_logo;
                // 删除原来的图片
                unlink('./' . $param['goods_old_logo']);
            } else {
                echo "没有文件上传";// 不需要验证文件信息、删除文件验证的规则
                // 走正常
                validate(\app\admin\validate\Goods::class)->scene('update')->check($param);
            }
        } catch (ValidateException $exception) {
            dd($exception->getError());
        }

//        if (isset($_FILES['goods_logo']) && !empty($_FILES['goods_logo'])) {
//            // 修改了文件
//            // 是否修改文件
//            // 重新上传
//            $goods_logo  = Filesystem::disk('public')->putFile('image',$request->file('goods_logo'));
//            $param['goods_logo'] = '/storage/'.$goods_logo;
//            // 删除原来的图片
//            unlink('./'.$param['goods_old_logo']);
//        } else {
//            // 不修改文件
//            $param['goods_logo'] = $param['goods_old_logo'];//不修改、路径还是原来的路径
//        }

        # 验证表单的参数
//        第一步 创建验证器 php think make:validate admin@Goods
        // 第二步 配置验证信息
//        rule 验证规则 message 验证不通过的错误信息
        // 使用什么验证器、验证什么参数 goods验证器类型、验证表单数据、后续继续完善
//        try {
//            // 第三步 使用验证器
//            validate(\app\admin\validate\Goods::class)->check($param);
//        }catch (ValidateException $exception) {
//            dd($exception->getError());
//        }

        // 更新入库
//        这种方式是最佳的更新方式。
        $id = $request->post('id');
        $goods = \app\admin\model\Goods::find($id);
        $goods->goods_name = $param['goods_name'];
        $goods->goods_price = $param['goods_price'];
        $goods->goods_number = $param['goods_number'];
        $goods->goods_logo = $param['goods_logo'];
        $result = $goods->save();
        if ($result) {
            return redirect('/admin/goodsList');
        }
    }

    /**
     * 批量删除
     */
    public function batchDelete(){
        # 根据主键删除的是数字
        $ids = \request()->post('ids');
        # 方法一
//        $goods = \app\admin\model\Goods::find($id);
//        $result = $goods->delete();// 删除后的结果
        # 方法二
        $result = \app\admin\model\Goods::destroy($ids);

        if ($result) {
            return json(['code' => 200, 'msg' => 'ok', 'data' => []]);

        } else {
            return json(['code' => 2001, 'msg' => '删除失败', 'data' => []]);
        }
    }

    /**
     * 公共函数的定义与使用
     * 单应用是在app/common.php
     * 这个位置是多应用 app/admin/common.php
     * 在某个应用下面定义、只能在某个应用下面使用
     */
    public function common(){
        $a = \request()->get('a');
        $b = \request()->get('b');
        # 调用公共方法 加法
        return add($a,$b);
    }

}
